package br.com.util;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;

import br.com.logistica.enums.EnumStatusNota;
import br.com.logistica.vo.Cliente;
import br.com.logistica.vo.Endereco;
import br.com.logistica.vo.NotaFiscal;
import br.com.logistica.vo.Produto;

/**
 * É obrigatório fazer uma derivação desta classe Atributos: error do tipo
 * String e nfvo do tipo NotaFiscal OBS: A classe possui o atributo "error" onde
 * fica gravado a exceção como String
 */
public abstract class Leitor {
	/**
	 * doError faça as tratativas de erro aqui. Este método é invocado quando
	 * alguma exceção ocorre durante o Parse
	 */
	protected abstract void doError();

	/**
	 * Este método é invocado quando ocorre tudo bem durante o parse
	 */
	protected abstract void doRight();

	private LinkedList<String> error=null;
	private NotaFiscal nfvo;

	/**
	 * Usado para capturar o parse corrente O mesmo só pode ser obtido uma vez
	 * 
	 * @return NotaFiscal
	 */
	public NotaFiscal getNota() {
		return nfvo;
	}

	/**
	 * Usado para capturar o erro corrente O mesmo só pode ser obtido uma única
	 * vez
	 * 
	 * @return O erro do corrente
	 */
	public LinkedList<String> getError() {
		return error;
	}

	/**
	 * Esse método faz o parse do arquivo XML para NotaFiscal
	 * 
	 * @param nome
	 *            - Caminho completo de onde se encontra o arquivo xml a ser
	 *            lido
	 */
	public void parser(String nome) {
		error=new LinkedList<String>();
		nfvo=null;
		NfeProc prenota=null;
		try {
			File arq = new File(nome);
			JAXBContext jaxbC = JAXBContext.newInstance(NfeProc.class);
			Unmarshaller junMarsha = jaxbC.createUnmarshaller();
			prenota = (NfeProc) junMarsha.unmarshal(arq);
		}
		catch (UnmarshalException ue) {
			if (ue.getCause() != null)
				error.add(ue.getCause().getMessage());
			else
				error.add(ue.getMessage());
			nfvo=null;
			this.doError();
			return;
		}catch (JAXBException je){
			if(je.getCause()!=null)
				error.add(je.getCause().getMessage());
			else
				error.add(je.getMessage());
			nfvo=null;
			this.doError();
			return;
		}
		
		try{
			nfvo = new NotaFiscal();
			if(prenota.getPkgnota()==null)
				error.add(Utility.tagErrorGen("NFe"));
			if(prenota.getPkgnota().getNotaemsi()==null)
				error.add(Utility.tagErrorGen("infNFe"));
			
			NF nf = prenota.getPkgnota().getNotaemsi();
			if(nf.getId()==null)error.add("Atributo Id inválido");
			nf.setId(nf.getId().trim());
			nfvo.setIdNotaFiscal(Integer.parseInt(nf.getId().substring(nf.getId().length() - 9)));
			
			if(nf.getIde()==null)
				error.add(Utility.tagErrorGen("ide"));
			
			if (nf.getIde().getNnf() <= 0)
				error.add(Utility.tagErrorGen("nNF"));
			
			nfvo.setnNF(nf.getIde().getNnf());
			if (nf.getIde().getDemi() == null)
				error.add(Utility.tagErrorGen("dEmi"));
			nfvo.setDtEmissao(nf.getIde().getDemi());
			if(nf.getTotal()==null)error.add(Utility.tagErrorGen("total"));
			if(nf.getTotal().getValor()==null)error.add(Utility.tagErrorGen("ICMSTot"));
			if (nf.getTotal().getValor().getVnf() <= 0)
				error.add(Utility.tagErrorGen("vNF"));
			nfvo.setValorTotal(nf.getTotal().getValor().getVnf());
			if(nf.getTransp()==null)error.add(Utility.tagErrorGen("transp"));
			if(nf.getTransp().getVol()==null)error.add(Utility.tagErrorGen("vol"));
			if (nf.getTransp().getVol().getqVol() <= 0)
				error.add(Utility.tagErrorGen("qVol"));
			nfvo.setQtdVol(nf.getTransp().getVol().getqVol());
			if (nf.getTransp().getVol().getPesoB() <= 0)
				error.add(Utility.tagErrorGen("pesoB"));
			nfvo.setPesoBruto(nf.getTransp().getVol().getPesoB());
			if (nf.getTransp().getVol().getPesoL() <= 0)
				error.add(Utility.tagErrorGen("pesoL"));
			nfvo.setPesoLiq(nf.getTransp().getVol().getPesoL());
			if (nf.getTransp().getVol().getEsp() == null)
				error.add(Utility.tagErrorGen("esp"));
			nfvo.setEsp(nf.getTransp().getVol().getEsp().trim());
			if(nf.getDet()==null)error.add(Utility.tagErrorGen("det"));
			if (nf.getDet().size() <= 0)
				error.add("A nota não tem produtos");
			
			nfvo.setQtdProduto(nf.getDet().size());

			/*---------------------------Montar lista de produtos-----------------------------------*/
			Iterator<Det> it = nf.getDet().iterator();
			Det x = null;
			Produto prodvo = null;
			String excep;
			HashMap<String, Integer> todos = nf.contarProdutos();
			List<Produto> passar = new ArrayList<Produto>();
			MasterLoop:while (it.hasNext()) {
				x = it.next();
				if(x==null||x.getNitem()<=0)error.add(Utility.tagErrorGen("det"));
				excep = "<det nItem=\"" + x.getNitem() + "\"><";
				if(x.getProd()==null)error.add(Utility.tagErrorGen(excep+"prod>"));
				prodvo = new Produto();
				if (x.getProd().getCfop() <= 0)
					error.add(Utility.tagErrorGen(excep + "CFOP>"));
				prodvo.setCfop(x.getProd().getCfop());
				if (x.getProd().getcProd()==null)
					error.add(Utility.tagErrorGen(excep + "cProd>"));
				
				for(int i=0;i<passar.size();++i){
					if(passar.get(i).getCodProdutos().equals(x.getProd().getcProd().trim()))
						continue MasterLoop;
				}
				prodvo.setCodProdutos(x.getProd().getcProd().trim());
				if (x.getProd().getxProd()==null)
					error.add(Utility.tagErrorGen(excep + "xProd>"));
				prodvo.setDescricao(x.getProd().getxProd().trim());
				prodvo.setQtdProduto(todos.get(x.getProd().getcProd()).intValue());
				if (x.getProd().getUcom()==null)
					error.add(Utility.tagErrorGen(excep + "uCom>"));
				prodvo.setUnCom(x.getProd().getUcom().trim());
				if (x.getProd().getVprod() < 0)
					error.add(Utility.tagErrorGen(excep + "vProd>"));
				prodvo.setValorProduto(todos.get(x.getProd().getcProd()).intValue() * x.getProd().getVprod());
				prodvo.setValorUnitario(x.getProd().getVprod());
				prodvo.setNotasFiscais(new ArrayList<NotaFiscal>());
				prodvo.getNotasFiscais().add(nfvo);
				passar.add(prodvo);
			}
			nfvo.setProdutos(passar);
			// --------------------------------------FIM-----------------------------------------

			nfvo.setEnumStatusNota(EnumStatusNota.ENVIADO);
			if (nf.getDest() == null)
				error.add(Utility.tagErrorGen("dest"));
			// ----------------Montar-destinatario--------------------------------------
			Cliente dest = new Cliente();
			excep=nf.getDest().getCnpj();
			if (excep==null||!Utility.ValidaCNPJ(excep))
				error.add(Utility.tagErrorGen("CNPJ"));
			dest.setCnpj(excep);
			if (nf.getDest().getIe()==null||!Utility.checkOnlyNumber(nf.getDest().getIe().trim()))
				error.add(Utility.tagErrorGen("IE"));
			dest.setInscricaoEstadual(nf.getDest().getIe().trim());
			if (nf.getDest().getxNome()==null)
				error.add(Utility.tagErrorGen("xNome"));
			dest.setNome(nf.getDest().getxNome().trim());

			// ------------------Endereco------------------------------------
			Endereco end = new Endereco();
			if(nf.getDest().getEnderDest()==null)error.add(Utility.tagErrorGen("enderDest"));
			if (nf.getDest().getEnderDest().getxBairro()==null)
				error.add(Utility.tagErrorGen("xBairro"));
			end.setBairro(nf.getDest().getEnderDest().getxBairro().trim());
			excep=nf.getDest().getEnderDest().getCEP();
			if(excep==null||(excep=excep.trim()).length()!=8||!Utility.checkOnlyNumber(nf.getDest().getEnderDest().getCEP()))
				error.add(Utility.tagErrorGen("CEP"));
			end.setCep(excep);
			if (nf.getDest().getEnderDest().getxLgr()==null)
				error.add(Utility.tagErrorGen("xLgr"));
			end.setLogradouro(nf.getDest().getEnderDest().getxLgr().trim());
			if (nf.getDest().getEnderDest().getxMun()==null)
				error.add(Utility.tagErrorGen("xMun"));
			end.setMunicipio(nf.getDest().getEnderDest().getxMun().trim());
			if (nf.getDest().getEnderDest().getNro()==null)
				error.add(Utility.tagErrorGen("nro"));
			end.setNumero(nf.getDest().getEnderDest().getNro().trim());
			if (nf.getDest().getEnderDest().getxPais()==null)
				error.add(Utility.tagErrorGen("xPais"));
			end.setPais(nf.getDest().getEnderDest().getxPais().trim());
			excep=nf.getDest().getEnderDest().getUF();
			if (excep==null||(excep=excep.trim()).length()!=2)
				error.add(Utility.tagErrorGen("UF"));
			end.setUf(excep);
			end.setClientes(dest);
			dest.setEnderecos(new ArrayList<Endereco>());
			dest.getEnderecos().add(end);
			nfvo.setDestinatario(dest);
			// -------------------FIM------------------------------
			
			if(error.size()>0)
				this.doError();
			else
				this.doRight();
		}
		catch (Throwable t) {
			error.add(t.getMessage());
			this.doError();
			nfvo=null;
		}
	}

}
